package cn.itcast.zt.java8.stream;

import cn.hutool.json.JSONUtil;
import org.junit.Test;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import static java.util.Comparator.comparingLong;
import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.toCollection;

/**
 * list集合去重的方法
 * create by zhangtian105 on 2022/3/27
 */
public class ListFilterRepeat {
    // 取出list集合中的重复String
    @Test
    public void filterListString() {
        List<String> strList = new ArrayList<String>() {{
            add("a") ;
            add("b") ;
            add("c") ;
            add("d") ;
            add("a") ;
        }};

        // 传统方式
        Set<String> set = new LinkedHashSet<>() ;
        set.addAll(strList) ;
        // list不能是只读的list
        strList.clear();

        strList.addAll(set) ;

        System.out.println("传统消除重复记录:" +strList);

        // java8方式
        List<String> strList2 = new ArrayList<String>() {{
            add("a") ;
            add("b") ;
            add("c") ;
            add("d") ;
            add("a") ;
        }};

        System.out.println("java8消除重复记录:"+strList2.stream().distinct().collect(Collectors.toList()));
    }

    /**
     * List中对象去重
     * 重写PersonBean对象的equals()方法和hashCode()方法
     */
    @Test
    public void filterListObj() {
        PersonBean p1 = new PersonBean(1l, "jack");
        PersonBean p2 = new PersonBean(3l, "jack chou");
        PersonBean p3 = new PersonBean(2l, "tom");
        PersonBean p4 = new PersonBean(4l, "hanson");
        PersonBean p5 = new PersonBean(5l, "胶布虫");

        List<PersonBean> persons = Arrays.asList(p1, p2, p3, p4, p5, p5, p1, p2, p2);
        List<PersonBean> personBeanList = new ArrayList<>() ;

        // 去重
        persons.stream().forEach(p -> {
            // List 的contains()方法底层实现使用对象的equals方法去比较的，其实重写equals()就好，但重写了equals最好将hashCode也重写了
            if (!personBeanList.contains(p)) {
                personBeanList.add(p) ;
            }
        });

        System.out.println("对象去重结果:"+ JSONUtil.toJsonStr(personBeanList));
    }

    /**
     * 通过对象的属性去重
     * 根据Person对象的id
     */
    @Test
    public void filterListBiObjField() {
        PersonBean p1 = new PersonBean(1l, "jack");
        PersonBean p2 = new PersonBean(3l, "jack chou");
        PersonBean p3 = new PersonBean(2l, "tom");
        PersonBean p4 = new PersonBean(4l, "hanson");
        PersonBean p5 = new PersonBean(5l, "胶布虫");
        List<PersonBean> persons = Arrays.asList(p1, p2, p3, p4, p5, p5, p1, p2, p2);

        // 方法一:根据id去重
        List<PersonBean> personBeans1 = removeDupliById(persons) ;
        System.out.println("方法一:根据id去重>>:"+personBeans1);

        // 方法二:根据id去重
        // 这段炫酷的代码是google的
        List<PersonBean> personBeans2 = persons.stream().collect(collectingAndThen(
                toCollection(() -> new TreeSet<>(comparingLong(PersonBean::getId))), ArrayList::new)
        ) ;
        System.out.println("方法二:根据id去重>>:"+personBeans2);

        // 方法三:根据id去重
        List<PersonBean> personBeans3 = persons.stream().filter(distinctByKey(p -> p.getId())).collect(Collectors.toList()) ;
        System.out.println("方法三:根据id去重>>:"+personBeans3);
    }

    /**
     * 通过Comparator比较器，比较对象属性，相同就返回0，达到过滤的目的
     * @param persons
     * @return
     */
    public static List<PersonBean> removeDupliById(List<PersonBean> persons) {
        Set<PersonBean> personSet = new TreeSet<>((o1, o2) -> o1.getId().compareTo(o2.getId()));
        personSet.addAll(persons);

        return new ArrayList<>(personSet);
    }

    public static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor) {
        Map<Object, Boolean> map = new ConcurrentHashMap<>();
        return t -> map.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }
}
